package com.sunyy.codegenerator.service.impl;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sunyy.codegenerator.common.MyException;
import com.sunyy.codegenerator.config.freemarker.FreeMarkerConfig;
import com.sunyy.codegenerator.dto.templateFileInfo.ListFileByGroupIdDto;
import com.sunyy.codegenerator.dto.templateFileInfo.SaveTemplateDto;
import com.sunyy.codegenerator.dto.templateFileInfo.UploadTemplateFilesDto;
import com.sunyy.codegenerator.entity.TemplateFileInfo;
import com.sunyy.codegenerator.mapper.TemplateFileInfoMapper;
import com.sunyy.codegenerator.service.CosService;
import com.sunyy.codegenerator.service.TemplateFileInfoService;
import com.sunyy.codegenerator.util.SnowflakeUtil;
import com.sunyy.codegenerator.vo.TreeNode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 60382
 * @description 针对表【template_file_info(模板文件信息)】的数据库操作Service实现
 * @createDate 2025-07-19 10:52:04
 */
@Slf4j
@Service
@Transactional
public class TemplateFileInfoServiceImpl extends ServiceImpl<TemplateFileInfoMapper, TemplateFileInfo> implements TemplateFileInfoService {
    private static final String bucketName = "code-generator";

    @Resource
    private CosService cosService;

    @Resource
    private FreeMarkerConfig freeMarkerConfig;

    @Override
    public void saveTemplate(SaveTemplateDto dto) {
        // 1.1 通用参数校验
        if (dto == null) {
            throw new MyException("入参不能为空!!");
        }
        if (dto.getGroupId() == null) {
            throw new MyException("入参【groupId】不能为空!!");
        }
        // 2. 判断是否新增
        boolean isNewOne = dto.getPkid() == null || dto.getPkid() < 0;
        if (isNewOne) {
            // 2.1 新增
            // 2.1.1 新增参数校验
            if (StringUtils.isBlank(dto.getTemplateFileName())) {
                throw new MyException("入参【templateFileName】不能为空!!");
            }
            if (StringUtils.isBlank(dto.getFileDirPath())) {
                throw new MyException("入参【fileDirPath】不能为空!!");
            }
            if (StringUtils.isBlank(dto.getTemplateFileContent()) && (dto.getUploadTemplateFile() == null || dto.getUploadTemplateFile().isEmpty())) {
                throw new MyException("模板文件内容不能为空!!");
            }
            // 2.1.2 设置主键
            dto.setPkid(SnowflakeUtil.getId());
            // 2.1.3 设置创建人和修改人
        } else {
            // 2.2 修改
            // 2.2.1 设置修改人
        }
        // 3. 构建保存实体
        TemplateFileInfo fileInfo = new TemplateFileInfo();
        BeanUtils.copyProperties(dto, fileInfo);
        if (StringUtils.isNotBlank(fileInfo.getFileDirPath())) {
            Path path = Paths.get(fileInfo.getFileDirPath().trim());
            fileInfo.setFileDirPath(path.toString().replace("\\", "/"));
        }
        // 4. 如果上传的模板文件不为空则保存模板文件
        if (dto.getUploadTemplateFile() != null && !dto.getUploadTemplateFile().isEmpty()) {
            byte[] bytes = null;
            try {
                bytes = dto.getUploadTemplateFile().getBytes();
                // 判断mime类型
                Tika tika = new Tika();
                String mimeType = tika.detect(new ByteArrayInputStream(bytes));
                if (!freeMarkerConfig.getAllowMimeType().contains(mimeType)) {
                    throw new MyException("MIME类型校验不通过!!");
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new MyException("获取模板文件字节数组失败!!");
            }
            if (!isNewOne) {
                // 4.1 不是新增，则需要删除原来的模板文件
                TemplateFileInfo info = getById(dto.getPkid());
                cosService.deleteFile(bucketName, info.getFileUrl());
            }
            // 4.2 设置上传文件的文件名
            int i = dto.getTemplateFileName().lastIndexOf('.');
            String uploadFileName = "/templateFile/"
                    + dto.getTemplateFileName()
                    + "-" + dto.getPkid()
                    + (i == -1 ? "" : dto.getTemplateFileName().substring(i));
            // 4.3 上传文件
            cosService.uploadFile(bucketName, uploadFileName, bytes);
            // 4.4 设置模板url
            fileInfo.setFileUrl(uploadFileName);
        }
        // 5. 文本内容不为空且不是通过文件上传的方式保存
        if (StringUtils.isBlank(fileInfo.getFileUrl()) && StringUtils.isNotBlank(dto.getTemplateFileContent())) {
            // 5.1  设置上传文件的文件名
            int i = dto.getTemplateFileName().lastIndexOf('.');
            String uploadFileName = "/templateFile/"
                    + dto.getTemplateFileName()
                    + "-" + dto.getPkid()
                    + (i == -1 ? "" : dto.getTemplateFileName().substring(i));
            // 5.2 上传文件
            cosService.uploadFile(bucketName, uploadFileName, dto.getTemplateFileContent().getBytes(StandardCharsets.UTF_8));
            // 5.3 设置模板url
            fileInfo.setFileUrl(uploadFileName);
        }
        // 6. 保存
        if (isNewOne) {
            save(fileInfo);
        } else {
            updateById(fileInfo);
        }
    }

    @Override
    public void deleteTemplate(String id) {
        if (id == null) {
            return;
        }
        Set<String> ids = Arrays.stream(id.split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        if (ids.isEmpty()) {
            return;
        }
        List<TemplateFileInfo> info = listByIds(ids);
        if (info == null || info.isEmpty()) {
            return;
        }
        // 删除模板文件
        info.forEach(one -> {
            try {
                if (StringUtils.isNotBlank(one.getFileUrl())) {
                    cosService.deleteFile(bucketName, one.getFileUrl());
                }
            } catch (Throwable e) {
                log.error("删除模板文件失败：{}", one.getFileUrl(), e);
            }

        });
        // 逻辑删除记录
        removeByIds(ids);
    }

    @Override
    public void uploadTemplateFiles(UploadTemplateFilesDto dto) {
        // 1. 参数校验
        if (dto == null) {
            throw new MyException("入参不能为空!!");
        }
        if (dto.getFiles() == null || dto.getFiles().length == 0) {
            return;
        }
        if (dto.getGroupId() == null || dto.getGroupId() < 0) {
            throw new MyException("入参【groupId】非法!!");
        }
        // 2. 遍历文件校验mime
        List<Path> unzipList = new ArrayList<>();
        Map<String, byte[]> fileList = new HashMap<>();
        for (MultipartFile file : dto.getFiles()) {
            byte[] bytes = null;
            try {
                bytes = file.getBytes();
                Tika tika = new Tika();
                String mimeType = tika.detect(new ByteArrayInputStream(bytes));
                if (MediaType.APPLICATION_ZIP.toString().equals(mimeType)) {
                    Path rootPath = Paths.get(freeMarkerConfig.getOutputPath(), "uploadUnzip", String.valueOf(System.currentTimeMillis()));
                    unzipList.add(rootPath);
                    // 解压
                    ZipUtil.unzip(new ByteArrayInputStream(bytes), rootPath.toFile(), StandardCharsets.UTF_8);
                    Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
                        @NotNull
                        @Override
                        public FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException {
                            Tika innerTika = new Tika();
                            String innerMimeType = innerTika.detect(file);
                            if (!freeMarkerConfig.getAllowMimeType().contains(innerMimeType)) {
                                throw new MyException("文件: " + file.getName(file.getNameCount() - 1) + " 的MIME类型校验不通过!!");
                            }
                            return super.visitFile(file, attrs);
                        }
                    });
                } else {
                    if (!freeMarkerConfig.getAllowMimeType().contains(mimeType)) {
                        throw new MyException("文件: " + file.getOriginalFilename() + " 的MIME类型校验不通过!!");
                    }
                    fileList.put(file.getOriginalFilename(), bytes);
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new MyException("IO异常!!", e);
            }
        }
        // 3. 校验通过，上传文件
        List<TemplateFileInfo> uploadFile = new ArrayList<>();
        // 3.1 上传压缩包中的文件
        for (Path unzipPath : unzipList) {
            try {
                Files.walkFileTree(unzipPath, new SimpleFileVisitor<Path>() {
                    @NotNull
                    @Override
                    public FileVisitResult visitFile(@NotNull Path path, @NotNull BasicFileAttributes attrs) throws IOException {
                        File file = path.toFile();
                        // 上传文件
                        TemplateFileInfo info = new TemplateFileInfo();
                        info.setPkid(SnowflakeUtil.getId());
                        info.setTemplateFileName(file.getName());
                        info.setFileUrl("/templateFile/batchUpload/" + info.getPkid() + "-" + file.getName());
                        info.setGroupId(dto.getGroupId());
                        info.setFileDirPath(unzipPath.relativize(path).toString().replace(file.getName(), ""));
                        cosService.uploadFile(bucketName, info.getFileUrl(), file);
                        uploadFile.add(info);
                        return super.visitFile(path, attrs);
                    }
                });
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new MyException("IO异常!!", e);
            }
            FileUtil.del(unzipPath);
        }
        // 3.2 上传单个文件
        for (Map.Entry<String, byte[]> one : fileList.entrySet()) {
            TemplateFileInfo info = new TemplateFileInfo();
            info.setPkid(SnowflakeUtil.getId());
            info.setTemplateFileName(one.getKey());
            info.setFileUrl("/templateFile/batchUpload/" + info.getPkid() + "-" + info.getTemplateFileName());
            info.setGroupId(dto.getGroupId());
            cosService.uploadFile(bucketName, info.getFileUrl(), one.getValue());
            uploadFile.add(info);
        }
        // 4. 保存
        saveBatch(uploadFile);

    }

    @Override
    public List<TreeNode> listFileTreeByGroupId(ListFileByGroupIdDto dto) {
        if (dto == null) {
            throw new MyException("入参不能为空!!");
        }
        if (dto.getGroupId() == null || dto.getGroupId() < 0) {
            throw new MyException("入参【groupId】非法!!");
        }
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("group_id", dto.getGroupId());
        List<TemplateFileInfo> fileList = list(wrapper);
        // 存储节点名称全路径
        Map<String, TreeNode> nodeMap = new HashMap<>();
        for (TemplateFileInfo file : fileList) {
            // 记录路径
            StringBuilder sb = new StringBuilder();
            String parent = null;
            String one = Paths.get("/".equals(file.getFileDirPath()) ? "" : file.getFileDirPath(), file.getTemplateFileName()).toString().replace("\\", "/");
            String[] split = one.split("/");
            for (int i = 0; i < split.length; i++) {
                String s = split[i];
                if (StringUtils.isBlank(s)) {
                    continue;
                }
                // 记录全路径
                sb.append("/").append(s);
                if (!nodeMap.containsKey(sb.toString())) {
                    TreeNode treeNode = new TreeNode();
                    treeNode.setParent(parent);
                    parent = sb.toString();
                    treeNode.setLabel(s);
                    treeNode.setId(parent);
                    nodeMap.put(parent, treeNode);
                    if ((i == split.length - 1)) {
                        // 文件
                        treeNode.setIsFile("1");
                        treeNode.setId(file.getPkid().toString());
                    } else {
                        // 目录
                        treeNode.setIsFile("0");
                    }
                } else {
                    parent = sb.toString();
                }
            }
        }
        List<TreeNode> rootNodeList = new ArrayList<>();
        for (Map.Entry<String, TreeNode> entry : nodeMap.entrySet()) {
            TreeNode treeNode = entry.getValue();
            String parent = treeNode.getParent();
            if (parent != null) {
                TreeNode parentNode = nodeMap.get(parent);
                if (parentNode.getChildren() == null) {
                    parentNode.setChildren(new ArrayList<>());
                }
                parentNode.getChildren().add(treeNode);
            } else {
                rootNodeList.add(treeNode);
            }

        }
        return rootNodeList;
    }

    @Override
    public SaveTemplateDto getFileContentById(Long id) {
        if (id == null || id < 0) {
            throw new MyException("入参【id】不能为空!!");
        }
        TemplateFileInfo info = getById(id);
        if (info == null) {
            throw new MyException("文件不存在!!");
        }
        SaveTemplateDto dto = new SaveTemplateDto();
        byte[] fileBytes = cosService.getFileBytes(bucketName, info.getFileUrl());
        dto.setPkid(info.getPkid());
        dto.setTemplateFileName(info.getTemplateFileName());
        dto.setTemplateFileContent(new String(fileBytes, StandardCharsets.UTF_8));
        return dto;
    }
}




